from __future__ import annotations

"""
依赖与权限判断
--------------
职责：
- 统一从 Header/Cookie 提取访问令牌并解析当前用户
- 提供 `require_admin` 与 `require_user` 作为路由依赖

说明：
- 优先使用 Bearer Token，其次使用 Cookie 中的 `access_token`
"""

from typing import Annotated, Optional

from fastapi import Cookie, Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy import select
from sqlalchemy.exc import NoResultFound
from sqlalchemy.ext.asyncio import AsyncSession

from .db import get_session
from .models import User
from .security.auth import AuthError, decode_token

bearer_scheme = HTTPBearer(auto_error=False)


async def _extract_token(
    credentials: Optional[HTTPAuthorizationCredentials],
    cookie_token: Optional[str],
) -> str:
    if credentials:
        return credentials.credentials
    if cookie_token:
        return cookie_token
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Not authenticated",
    )


async def get_current_user(
    credentials: Annotated[Optional[HTTPAuthorizationCredentials], Depends(bearer_scheme)],
    session: Annotated[AsyncSession, Depends(get_session)],
    cookie_token: Annotated[Optional[str], Cookie(alias="access_token")] = None,
) -> User:
    token = await _extract_token(credentials, cookie_token)
    try:
        payload = decode_token(token, expected_type="access")
    except AuthError as exc:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc

    subject = payload.get("sub")
    if subject is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")

    stmt = select(User).where(User.id == int(subject))
    try:
        result = await session.execute(stmt)
        user = result.scalar_one()
    except (NoResultFound, ValueError):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
    return user


def require_admin(user: Annotated[User, Depends(get_current_user)]) -> User:
    if user.role != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden")
    return user


def require_user(user: Annotated[User, Depends(get_current_user)]) -> User:
    return user


async def get_db_session() -> AsyncSession:
    async with get_session() as session:
        yield session
